package com.lingchou.cloud.process.client.service.impl;

import cn.hutool.core.util.StrUtil;
import com.lc.common.bean.response.Result;
import com.lingchou.cloud.process.api.dto.result.*;
import com.lingchou.cloud.process.api.dto.result.base.ProcessTaskRecordVo;
import com.lingchou.cloud.process.client.dao.DefinitionSupplementaryDao;
import com.lingchou.cloud.process.client.entity.DefinitionSupplementary;
import com.lingchou.cloud.process.client.service.ProcessUtilService;
import com.lingchou.cloud.process.client.util.LocalDateAndLocalDateTimeUtil;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.variable.api.history.HistoricVariableInstance;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class ProcessUtilServiceImpl implements ProcessUtilService {

    private final RepositoryService repositoryService;
    private final TaskService taskService;
    private final HistoryService historyService;
    private final DefinitionSupplementaryDao definitionSupplementaryDao;

    public ProcessUtilServiceImpl(RepositoryService repositoryService, TaskService taskService, HistoryService historyService, DefinitionSupplementaryDao definitionSupplementaryDao) {
        this.repositoryService = repositoryService;
        this.taskService = taskService;
        this.historyService = historyService;
        this.definitionSupplementaryDao = definitionSupplementaryDao;
    }

    @Override
    public ProcessDefinition getProcessDefinition(String deploymentId) {
        return StrUtil.isEmpty(deploymentId) ? null :
                repositoryService.createProcessDefinitionQuery()
                        .deploymentId(deploymentId).latestVersion().singleResult();
    }

    @Override
    public ProcessDefinitionDTO buildProcessDefinition(ProcessDefinition processDefinition) {
        if (processDefinition == null) {
            return null;
        }
        return ProcessDefinitionDTO.builder()
                .id(processDefinition.getId())
                .category(processDefinition.getCategory())
                .name(processDefinition.getName())
                .key(processDefinition.getKey())
                .description(processDefinition.getDescription())
                .version(processDefinition.getVersion())
                .resourceName(processDefinition.getResourceName())
                .deploymentId(processDefinition.getDeploymentId())
                .diagramResourceName(processDefinition.getDiagramResourceName())
                .suspended(processDefinition.isSuspended())
                .tenantId(processDefinition.getTenantId())
                .build();
    }

    @Override
    public Result<ProcessDefinitionDTO> packageReturnProcessDefinition(ProcessDefinition processDefinition, String errorMessage) {
        ProcessDefinitionDTO processDefinitionDTO = buildProcessDefinition(processDefinition);
        return processDefinitionDTO == null ? Result.failure(errorMessage) : Result.success(processDefinitionDTO);
    }

    @Override
    public Result<ProcessDefinitionDTO> packageProcessDefinitionByDeploymentId(String deploymentId, String errorMessage) {
        ProcessDefinition processDefinition = getProcessDefinition(deploymentId);
        return packageReturnProcessDefinition(processDefinition, errorMessage);
    }

    @Override
    public ProcessInstanceDTO buildProcessInstance(ProcessInstance processInstance) {
        if (processInstance == null) {
            return null;
        }
        return ProcessInstanceDTO.builder()
                .id(processInstance.getId())
                .ended(processInstance.isEnded())
                .activityId(processInstance.getActivityId())
                .processDefinitionId(processInstance.getProcessDefinitionId())
                .processDefinitionName(processInstance.getProcessDefinitionName())
                .processDefinitionKey(processInstance.getProcessDefinitionKey())
                .processDefinitionVersion(processInstance.getProcessDefinitionVersion())
                .deploymentId(processInstance.getDeploymentId())
                .businessKey(processInstance.getBusinessKey())
                .suspended(processInstance.isSuspended())
                .processVariables(processInstance.getProcessVariables())
                .tenantId(processInstance.getTenantId())
                .name(processInstance.getName())
                .description(processInstance.getDescription())
                .localizedDescription(processInstance.getLocalizedDescription())
                .localizedName(processInstance.getLocalizedName())
                .startTime(LocalDateAndLocalDateTimeUtil.dateToLocalDateTime(processInstance.getStartTime()))
                .startUserId(processInstance.getStartUserId())
                .callbackId(processInstance.getCallbackId())
                .callbackType(processInstance.getCallbackType())
                .build();
    }

    @Override
    public List<ProcessInstanceDTO> buildProcessInstanceList(List<ProcessInstance> processInstances) {
        if (processInstances != null && !processInstances.isEmpty()) {
            return processInstances.stream().map(this::buildProcessInstance).collect(Collectors.toList());
        }
        return new ArrayList<>();
    }

    @Override
    public Result<ProcessInstanceDTO> packageReturnProcessInstance(ProcessInstance processInstance, String errorMessage) {
        ProcessInstanceDTO processInstanceDTO = buildProcessInstance(processInstance);
        return processInstanceDTO == null ? Result.failure("流程实例不存在") : Result.success(processInstanceDTO);
    }

    @Override
    public Result<List<ProcessInstanceDTO>> packageReturnProcessInstanceList(List<ProcessInstance> processInstances, String errorMessage) {
        List<ProcessInstanceDTO> processInstanceDTOS = buildProcessInstanceList(processInstances);
        return processInstanceDTOS.isEmpty() ? Result.failure(errorMessage) : Result.success(processInstanceDTOS);
    }

    @Override
    public TaskDTO buildTaskDTO(Task task,DefinitionSupplementary definitionSupplementary) {
        if (task == null) {
            return null;
        }
        //变量
        String selectionRange = definitionSupplementary == null ? null : definitionSupplementary.getSelectionRange();
        return TaskDTO.builder()
                .id(task.getId())
                .taskDefId(task.getTaskDefinitionKey())
                .name(task.getName())
                .description(task.getDescription())
                .priority(task.getPriority())
                .owner(task.getOwner())
                .assignee(task.getAssignee())
                .processInstanceId(task.getProcessInstanceId())
                .processDefinitionId(task.getProcessDefinitionId())
                .createTime(LocalDateAndLocalDateTimeUtil.dateToLocalDateTime(task.getCreateTime()))
                .dueDate(LocalDateAndLocalDateTimeUtil.dateToLocalDate(task.getDueDate()))
                .parentTaskId(task.getParentTaskId())
                .tenantId(task.getTenantId())
                .taskLocalVariables(task.getTaskLocalVariables())
                .processVariables(task.getProcessVariables())
                .claimTime(LocalDateAndLocalDateTimeUtil.dateToLocalDateTime(task.getClaimTime()))
                .selectionRange(selectionRange)
                .build();
    }

    @Override
    public String getTaskDefinitionKey(String taskId) {
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        String taskDefinitionId;
        if (task == null) {
            HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery().taskId(taskId).singleResult();
            if (historicTaskInstance == null) {
                return null;
            }
            taskDefinitionId = historicTaskInstance.getTaskDefinitionKey();
        } else {
            taskDefinitionId = task.getTaskDefinitionKey();
        }
        return taskDefinitionId;
    }

    @Override
    public Result<TaskDTO> packageReturnTaskDTO(Task task, String errorMessage) {
        String taskDefinitionId = getTaskDefinitionKey(task.getId());
        DefinitionSupplementary definitionSupplementary = definitionSupplementaryDao.findByTaskDefId(taskDefinitionId);
        TaskDTO taskDTO = buildTaskDTO(task,definitionSupplementary);
        return taskDTO == null ? Result.failure(errorMessage) : Result.success(taskDTO);
    }

    @Override
    public List<TaskDTO> buildTaskDTOList(List<Task> tasks) {
        if (tasks != null && !tasks.isEmpty()) {
            List<String> taskIds = tasks.stream().map(Task::getTaskDefinitionKey).collect(Collectors.toList());
            Map<String, DefinitionSupplementary> map = definitionSupplementaryDao.findByTaskDefIdIn(taskIds)
                    .stream().collect(Collectors.toMap(DefinitionSupplementary::getTaskDefId, definitionSupplementary -> definitionSupplementary));
            return tasks.stream().map(task -> buildTaskDTO(task,map.get(task.getTaskDefinitionKey()))).collect(Collectors.toList());
        }
        return new ArrayList<>();
    }

    @Override
    public Result<List<TaskDTO>> packageReturnTaskDTOList(List<Task> tasks, String errorMessage) {
        List<TaskDTO> taskDTOS = buildTaskDTOList(tasks);
        return taskDTOS.isEmpty() ? Result.failure(errorMessage) : Result.success(taskDTOS);
    }

    @Override
    public HistoricTaskInstanceDTO buildHistoricTaskInstanceDTO(HistoricTaskInstance historicTaskInstance, DefinitionSupplementary definitionSupplementary) {
        if (historicTaskInstance == null) {
            return null;
        }
        //变量
        String selectionRange = definitionSupplementary == null ? null : definitionSupplementary.getSelectionRange();
        return HistoricTaskInstanceDTO.builder()
                .taskDefId(historicTaskInstance.getTaskDefinitionKey())
                .id(historicTaskInstance.getId())
                .name(historicTaskInstance.getName())
                .description(historicTaskInstance.getDescription())
                .priority(historicTaskInstance.getPriority())
                .owner(historicTaskInstance.getOwner())
                .assignee(historicTaskInstance.getAssignee())
                .processInstanceId(historicTaskInstance.getProcessInstanceId())
                .processDefinitionId(historicTaskInstance.getProcessDefinitionId())
                .createTime(LocalDateAndLocalDateTimeUtil.dateToLocalDateTime(historicTaskInstance.getCreateTime()))
                .dueDate(LocalDateAndLocalDateTimeUtil.dateToLocalDate(historicTaskInstance.getDueDate()))
                .parentTaskId(historicTaskInstance.getParentTaskId())
                .tenantId(historicTaskInstance.getTenantId())
                .taskLocalVariables(historicTaskInstance.getTaskLocalVariables())
                .processVariables(historicTaskInstance.getProcessVariables())
                .claimTime(LocalDateAndLocalDateTimeUtil.dateToLocalDateTime(historicTaskInstance.getClaimTime()))
                .deleteReason(historicTaskInstance.getDeleteReason())
                .endTime(LocalDateAndLocalDateTimeUtil.dateToLocalDateTime(historicTaskInstance.getEndTime()))
                .durationInMillis(historicTaskInstance.getDurationInMillis())
                .workTimeInMillis(historicTaskInstance.getWorkTimeInMillis())
                .selectionRange(selectionRange)
                .build();
    }

    @Override
    public List<HistoricTaskInstanceDTO> buildHistoricTaskInstanceDTOList(List<HistoricTaskInstance> historicTaskInstanceList) {
        if (historicTaskInstanceList != null && !historicTaskInstanceList.isEmpty()) {
            //获取任务定义ID
            List<String> taskDefinitionIds = historicTaskInstanceList.stream().map(HistoricTaskInstance::getTaskDefinitionKey).collect(Collectors.toList());
            Map<String, DefinitionSupplementary> map = definitionSupplementaryDao.findByTaskDefIdIn(taskDefinitionIds)
                    .stream().collect(Collectors.toMap(DefinitionSupplementary::getTaskDefId, definitionSupplementary -> definitionSupplementary));
            return historicTaskInstanceList.stream()
                    .map(historicTaskInstance -> buildHistoricTaskInstanceDTO(historicTaskInstance, map.get(historicTaskInstance.getTaskDefinitionKey())))
                    .collect(Collectors.toList());
        }
        return new ArrayList<>();
    }

    @Override
    public AllTaskDTO buildHistoricTaskInstanceToAllTaskDTO(HistoricTaskInstance historicTaskInstance) {
        if (historicTaskInstance == null) {
            return null;
        }
        //变量
        String taskDefinitionId = getTaskDefinitionKey(historicTaskInstance.getId());
        DefinitionSupplementary definitionSupplementary = definitionSupplementaryDao.findByTaskDefId(taskDefinitionId);
        String selectionRange = definitionSupplementary == null ? null : definitionSupplementary.getSelectionRange();
        return AllTaskDTO.builder()
                .id(historicTaskInstance.getId())
                .taskDefId(historicTaskInstance.getTaskDefinitionKey())
                .name(historicTaskInstance.getName())
                .description(historicTaskInstance.getDescription())
                .owner(historicTaskInstance.getOwner())
                .assignees(Collections.singletonList(historicTaskInstance.getAssignee()))
                .createTime(LocalDateAndLocalDateTimeUtil.dateToLocalDateTime(historicTaskInstance.getCreateTime()))
                .selectionRange(selectionRange)
                .build();
    }

    @Override
    public List<AllTaskDTO> buildHistoricTaskInstanceToAllTaskDTOs(List<HistoricTaskInstance> historicTaskInstances) {
        List<AllTaskDTO> allTaskDTOS = new ArrayList<>();
        //将HistoricTaskInstance转 AllTaskDTO
        if (historicTaskInstances != null && !historicTaskInstances.isEmpty()) {
            allTaskDTOS = historicTaskInstances.stream().map(this::buildHistoricTaskInstanceToAllTaskDTO).collect(Collectors.toList());
        }
        List<AllTaskDTO> finalAllTaskDTOS = new ArrayList<>();
        //
        if (!allTaskDTOS.isEmpty()) {
            //记录有序defId集合
            List<String> keys = allTaskDTOS.stream().map(AllTaskDTO::getTaskDefId).distinct().collect(Collectors.toList());
            //合并成map
            Map<String, List<AllTaskDTO>> taskMap = allTaskDTOS.stream().collect(Collectors.groupingBy(AllTaskDTO::getTaskDefId));
            //有序遍历keys
            keys.forEach(s -> {
                List<AllTaskDTO> taskDTOS = taskMap.get(s);
                //如果有重复的，则将把Assignees合并到集合的第一项
                if (taskDTOS.size() > 1) {
                    List<String> assignees = new ArrayList<>();
                    taskDTOS.forEach(t -> assignees.addAll(t.getAssignees()));
                    taskDTOS.get(0).setAssignees(assignees);
                }
                finalAllTaskDTOS.add(taskDTOS.get(0));
            });
        }
        return finalAllTaskDTOS;
    }

    @Override
    public ProcessTaskRecordVo buildUserTaskToProcessTaskRecordVo(UserTask userTask, String processInstanceId) {
        if (userTask == null) {
            return null;
        }
        //变量
        DefinitionSupplementary definitionSupplementary = definitionSupplementaryDao.findByTaskDefId(userTask.getId());
        //选人范围
        String selectionRange = definitionSupplementary.getSelectionRange();
        HistoricVariableInstance historicVariableInstance = historyService.createHistoricVariableInstanceQuery()
                .processInstanceId(processInstanceId)
                .variableName(definitionSupplementary.getNodeTypeParamName()).singleResult();
        //节点审批类型
        String typeValue = historicVariableInstance.getValue().toString();
        return ProcessTaskRecordVo.builder()
                .taskDefId(userTask.getId())
                .name(userTask.getName())
                .nodeType(typeValue)
                .selectionRange(selectionRange)
                .build();
    }

    @Override
    public List<ProcessTaskRecordVo> buildUserTasksToProcessTaskRecordVos(List<UserTask> userTasks, String processInstanceId) {
        if (userTasks != null && !userTasks.isEmpty()) {
            return userTasks.stream().map(userTask -> buildUserTaskToProcessTaskRecordVo(userTask, processInstanceId)).collect(Collectors.toList());
        }
        return new ArrayList<>();
    }

    @Override
    public AllTaskDTO buildUserTaskToAllTaskDTO(UserTask userTask) {
        if (userTask == null) {
            return null;
        }
        //变量
        DefinitionSupplementary definitionSupplementary = definitionSupplementaryDao.findByTaskDefId(userTask.getId());
        String varJson = definitionSupplementary == null ? null : definitionSupplementary.getSelectionRange();
        return AllTaskDTO.builder()
                .taskDefId(userTask.getId())
                .name(userTask.getName())
                .owner(userTask.getOwner())
                .assignees(Collections.singletonList(userTask.getAssignee()))
                .selectionRange(varJson)
                .build();
    }

    @Override
    public List<AllTaskDTO> buildUserTasksToAllTaskDTO(List<UserTask> userTasks) {
        if (userTasks != null && !userTasks.isEmpty()) {
            return userTasks.stream().map(this::buildUserTaskToAllTaskDTO).collect(Collectors.toList());
        }
        return new ArrayList<>();
    }

    @Override
    public Result<List<HistoricTaskInstanceDTO>> packageReturnHistoricTaskInstanceDTOList(List<HistoricTaskInstance> historicTaskInstanceList, String errorMessage) {
        List<HistoricTaskInstanceDTO> historicTaskInstanceDTOS = buildHistoricTaskInstanceDTOList(historicTaskInstanceList);
        return historicTaskInstanceDTOS.isEmpty() ? Result.failure(errorMessage) : Result.success(historicTaskInstanceDTOS);
    }

    @Override
    public HistoricProcessInstanceDTO buildHistoricProcessInstanceDTO(HistoricProcessInstance historicProcessInstance) {
        if (historicProcessInstance == null) {
            return null;
        }
        return HistoricProcessInstanceDTO.builder()
                .id(historicProcessInstance.getId())
                .businessKey(historicProcessInstance.getBusinessKey())
                .processDefinitionId(historicProcessInstance.getProcessDefinitionId())
                .processDefinitionName(historicProcessInstance.getProcessDefinitionName())
                .processDefinitionKey(historicProcessInstance.getProcessDefinitionKey())
                .processDefinitionVersion(historicProcessInstance.getProcessDefinitionVersion())
                .deploymentId(historicProcessInstance.getDeploymentId())
                .startTime(LocalDateAndLocalDateTimeUtil.dateToLocalDateTime(historicProcessInstance.getStartTime()))
                .endTime(LocalDateAndLocalDateTimeUtil.dateToLocalDateTime(historicProcessInstance.getEndTime()))
                .durationInMillis(historicProcessInstance.getDurationInMillis())
                .endActivityId(historicProcessInstance.getEndActivityId())
                .startUserId(historicProcessInstance.getStartUserId())
                .startActivityId(historicProcessInstance.getStartActivityId())
                .deleteReason(historicProcessInstance.getDeleteReason())
                .superProcessInstanceId(historicProcessInstance.getSuperProcessInstanceId())
                .tenantId(historicProcessInstance.getTenantId())
                .name(historicProcessInstance.getName())
                .description(historicProcessInstance.getDescription())
                .callbackId(historicProcessInstance.getCallbackId())
                .callbackType(historicProcessInstance.getCallbackType())
                .referenceId(historicProcessInstance.getReferenceId())
                .referenceType(historicProcessInstance.getReferenceType())
                .processVariables(historicProcessInstance.getProcessVariables())
                .build();
    }

    @Override
    public List<HistoricProcessInstanceDTO> buildHistoricProcessInstanceDTOList(List<HistoricProcessInstance> historicProcessInstance) {
        if (historicProcessInstance != null && !historicProcessInstance.isEmpty()) {
            return historicProcessInstance.stream().map(this::buildHistoricProcessInstanceDTO).collect(Collectors.toList());
        }
        return new ArrayList<>();
    }

    @Override
    public Result<List<HistoricProcessInstanceDTO>> packageReturnHistoricProcessInstanceDTOList(List<HistoricProcessInstance> historicProcessInstance, String errorMessage) {
        List<HistoricProcessInstanceDTO> historicProcessInstanceDTOS = buildHistoricProcessInstanceDTOList(historicProcessInstance);
        return historicProcessInstanceDTOS.isEmpty() ? Result.failure(errorMessage) : Result.success(historicProcessInstanceDTOS);
    }
}
